package Farsail::Config;

use strict; 
use warnings;

use Carp;
use YAML qw/LoadFile/;
use base 'AppConfig';
use UNIVERSAL;
use Farsail::Database;
use Farsail::Util qw/expand_file/;
use File::Glob qw/:glob/;
our $FARSAIL_CONFIG = 'farsail.conf';

sub new {
    my $_class = shift;
    my $class = ref $_class || $_class;
    my $rootdir = shift || confess "Need config root dir!\n";
    my $self = $class->SUPER::new( {
        CREATE => 1,
        CASE => 1,
        GLOBAL => {
            ARGCOUNT => AppConfig::ARGCOUNT_ONE,
        },
    }, 'include=s@' );
    $self->set('rootdir', $rootdir);
    $self->{STATE}{INCLUDES} = {};
    bless $self, $class;
    my $conf_file = expand_file( $FARSAIL_CONFIG, $rootdir);
    if ( !-e $conf_file ) {
        confess "Can't find farsail configuration file '$conf_file'!\n";
    }
    $self->file( $conf_file );
    return $self;
}

sub config_file {
    return $FARSAIL_CONFIG;
}

sub get {
    my $self = shift;
    my $name = shift;
    if ( $self->_exists( $name ) ) {
        return $self->SUPER::get($name);
    }
    else {
        return undef;
    }
}

sub remove {
    my $self = shift;
    my $re = shift;
    my $state = $self->{STATE};
    for my $v( grep { /$re/} keys %{$state->{VARIABLE}} ) {
        for ( qw/VARIABLE ALIASES ALIAS ARGS VALIDATE EXPAND ACTION ARGCOUNT/ ) {
            delete $state->{$_}{$v};
        }
    }
}

sub file {
    my $self = shift;
    my $state = $self->{STATE};
    for my $file ( @_ ) {
        next if exists $state->{INCLUDES}{$file};
        $state->{INCLUDES}{$file}++;
        $self->SUPER::file( UNIVERSAL::isa($file, 'Path::Class::File') ? $file->stringify : $file );
        if ( $self->_exists('include') ) {
            my $include = $self->include;
            push @{$state->{VARIABLE}{INC}}, @$include;
            $state->{VARIABLE}{include} = [];
            for ( @$include ) {
                my $i = expand_file($_, $self->rootdir);
                if ( -e $i ) {
                    $self->file($i);
                } else {
                    for ( bsd_glob($i, GLOB_ERROR) ) {
                        $self->file($_);
                    }
                }
            }
        }
    }
}

sub section {
    my $self = shift;
    return { $self->varlist('^'.quotemeta($_[0]."."), 1) };
}

sub get_expand_file {
    my $self = shift;
    my $name = shift;
    my $file = $self->get($name) || return;
    $file = expand_file($file, $self->rootdir);
    if ( @_ ) {
        $file = Path::Class::File->new($file, @_);
    }
    return $file;
}

sub database {
    my $self = shift;
    if ( !$self->_exists('databases') ) {
        my $dbconf = expand_file($self->get('databases_config_file'), $self->rootdir);
        my $databases = {};
        if ( -e $dbconf ) {
            eval {
                $databases = LoadFile($dbconf);
            };
            if ( $@ ) {
                confess "Load database config '$dbconf' failed\n";
            }
        }
        $self->set( 'databases', $databases );
    }
    if ( @_ ) {
        return Farsail::Database->new($self->get('databases')->{$_[0]});
    }
    return $self->get('databases');
}

sub cluster {
    my $self = shift;
    my $name = shift;
    my $cluster = $self->section( $name ? "cluster.$name" : 'cluster' );
    return unless %$cluster;
    if ( $name ) {
        my @nodes;
        for ( keys %$cluster ) {
            if ( /^nodes\.(\d+)$/ ) {
                $nodes[$1] = delete $cluster->{$_};
            }
        }
        $cluster->{nodes} = \@nodes;
    } else {
        my %clusters;
        for ( keys %$cluster ) {
            my @p = split /\./, $_, 2;
            next if @p < 2;
            if ( $p[1] =~ /^nodes\.(\d+)$/ ) {
                $clusters{$p[0]}{nodes}[$1] = $cluster->{$_};
            } else {
                $clusters{$p[0]}{$p[1]} = $cluster->{$_};
            }
        }
        $cluster = \%clusters;
    }
    return $cluster;
}

sub pbs {
    my $self = shift;
    my $pbs = $self->section('pbs');
    my @nodes;
    for ( keys %$pbs ) {
        if ( /nodes\.(\d+)$/ ) {
            $nodes[$1] = delete $pbs->{$_};
        }
    }
    $pbs->{nodes} = \@nodes;
    return $pbs;
}

sub log4perl {
    my $self = shift;
    my %config = $self->varlist('^log4perl\.');
    return $self->expand_log4perl_config(\%config);
}

sub expand_log4perl_config {
    my ($self, $config) = @_;
    my $str;
    my %appender;
    for ( keys %$config ) {
        if ( /^log4perl\.appender\./ ) {
            my @r = split /\./, $_, 4;
            if ( @r == 3 ) {
                $appender{$r[2]}{type} = $config->{$_};
            } else {
                $appender{$r[2]}{$r[3]} = $config->{$_};
            }
        } else {
            $str .= "$_ = $config->{$_}\n";
        }
    }
    my $expander;
    $expander = sub {
        my $app = shift;
        return $app if !exists $app->{isa};
        my $isa = $expander->($appender{$app->{isa}});
        for ( keys %$isa  ) {
            next if exists $app->{$_};
            $app->{$_} = $isa->{$_};
        }
        delete $app->{isa};
        return $app;
    };
    map { $expander->($_) } values %appender;
    for my $app ( keys %appender ) {
        $str .= "log4perl.appender.$app = " . delete($appender{$app}{'type'}). "\n";
        for ( keys %{$appender{$app}} ) {
            if ( $_ eq 'filename' ) {
                $appender{$app}{$_} = expand_file($appender{$app}{$_}, $self->rootdir);
            }
            $str .= "log4perl.appender.$app.$_ = $appender{$app}{$_}\n";
        }
    }
    return $str;
}

1;
